﻿using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net;
using System.Text.RegularExpressions;

namespace Devonline.Core;

/// <summary>
/// 验证类扩展方法
/// </summary>
public static class ValidateExtensions
{
    /// <summary>
    /// 全国手机号码号段
    /// 2022年国内运营商最全号段，联通, 移动, 电信, 广电四大运营商
    /// 最后更新时间：2022年的02月11日
    /// 涵盖联通, 移动, 电信, 广电各运营商的手机（终端）号段划分（普通卡, 虚拟卡, 物联网卡, 卫星电话卡）
    /// 1. 中国电信号段: 133, 1349(卫), 141(物), 149(物), 153, 162(虚), 1700(虚), 1701(虚), 1702(虚), 173, 1740(0-5)(卫), 177, 180, 181, 189, 190(5G), 191, 193, 199
    /// 2. 中国联通号段: 130, 131, 132, 140(物), 145, 146(物), 155, 156, 166, 167(虚), 1704(虚), 1707(虚), 1708(虚), 1709(虚)，171(虚), 175, 176, 185, 186, 196(5G)
    /// 3. 中国移动号段: 134(0-8), 135, 136, 137, 138, 139, 144(物), 147, 148(物), 150, 151, 152, 157, 158, 159, 165(虚), 1703(虚), 1705(虚), 1706(虚), 172(物), 178, 182, 183, 184, 187, 188, 195, 197(5G), 198
    /// 4. 中国广电号段: 192(5G)
    /// 5. 中国交通通信信息中心: 1749(卫)
    /// 6. 工业和信息化部应急通信保障中心: 1740(6-9)(卫), 1741(0-2)(卫)
    /// </summary>
    public static readonly string[] PhoneNumberSegment = new string[] {
        "133", "1349", "141", "149", "153", "162", "1700", "1701", "1702", "173", "1740", "177", "180", "181", "189", "190", "191", "193", "199",
        "130", "131", "132", "140", "145", "146", "155", "156", "166", "167", "1704", "1707", "1708", "1709", "171", "175", "176", "185", "186", "196",
        "134", "135", "136", "137", "138", "139", "144", "147", "148", "150", "151", "152", "157", "158", "159", "165", "1703", "1705", "1706", "172", "178", "182", "183", "184", "187", "188", "195", "197", "198",
        "192",
        "1749",
        "1740", "1741"
    };

    #region 匹配Email
    /// <summary>
    /// 匹配Email
    /// </summary>
    /// <param name="value">源字符串</param>
    /// <param name="isMatch">是否匹配成功，若返回true，则会得到一个Match对象，否则为null</param>
    /// <returns>匹配对象</returns>
    public static Match? MatchEmail(this string value, out bool isMatch)
    {
        if (string.IsNullOrEmpty(value))
        {
            isMatch = false;
            return null;
        }

        Match match = Regex.Match(value, @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
        isMatch = match.Success;
        return isMatch ? match : null;
    }
    /// <summary>
    /// 匹配Email
    /// </summary>
    /// <param name="value">源字符串</param>
    /// <returns>是否匹配成功</returns>
    public static bool IsEmail(this string value)
    {
        MatchEmail(value, out bool success);
        return success;
    }
    #endregion

    #region 匹配完整的URL
    /// <summary>
    /// 匹配完整格式的URL
    /// </summary>
    /// <param name="value">源字符串</param>
    /// <param name="isMatch">是否匹配成功，若返回true，则会得到一个Match对象，否则为null</param>
    /// <returns>匹配对象</returns>
    public static Uri? MatchUrl(this string value, out bool isMatch)
    {
        try
        {
            isMatch = true;
            return new Uri(value);
        }
        catch
        {
            isMatch = false;
            return null;
        }
    }
    /// <summary>
    /// 匹配完整格式的URL
    /// </summary>
    /// <param name="value">源字符串</param>
    /// <returns>是否匹配成功</returns>
    public static bool IsUrl(this string value)
    {
        MatchUrl(value, out var isMatch);
        return isMatch;
    }
    #endregion

    #region 校验IP地址的合法性
    /// <summary>
    /// 校验IP地址的正确性，同时支持IPv4和IPv6
    /// </summary>
    /// <param name="value">源字符串</param>
    /// <param name="isMatch">是否匹配成功，若返回true，则会得到一个Match对象，否则为null</param>
    /// <returns>匹配对象</returns>
    public static IPAddress? MatchIPAddress(this string value, out bool isMatch)
    {
        isMatch = IPAddress.TryParse(value, out var ip);
        return ip;
    }
    /// <summary>
    /// 校验IP地址的正确性，同时支持IPv4和IPv6
    /// </summary>
    /// <param name="value">源字符串</param>
    /// <returns>是否匹配成功</returns>
    public static bool IsIPAddress(this string value)
    {
        MatchIPAddress(value, out var success);
        return success;
    }
    #endregion

    #region 校验手机号码的正确性
    /// <summary>
    /// 匹配手机号码
    /// </summary>
    /// <param name="value">源字符串</param>
    /// <param name="isMatch">是否匹配成功，若返回true，则会得到一个Match对象，否则为null</param>
    /// <returns>匹配对象</returns>
    public static Match? MatchPhoneNumber(this string value, out bool isMatch)
    {
        if (string.IsNullOrWhiteSpace(value) || value.Length <= 11)
        {
            isMatch = false;
            return null;
        }

        Match match = Regex.Match(value, @"^((1[3,5,6,8][0-9])|(14[5,7])|(17[0,1,3,6,7,8])|(19[8,9]))\d{8}$");
        isMatch = match.Success;
        return isMatch ? match : null;
    }
    /// <summary>
    /// 匹配手机号码
    /// 我国手机使用的号码为11位，其中：
    /// 第1-3位-网络识别号；
    /// 第4-7位-地区编码；
    /// 第8-11位-用户号码。
    /// 手机号码前三位也叫做号段。
    /// 2022年国内运营商最全号段，联通, 移动, 电信, 广电四大运营商
    /// 最后更新时间：2022年的02月11日
    /// 涵盖联通, 移动, 电信, 广电各运营商的手机（终端）号段划分（普通卡, 虚拟卡, 物联网卡, 卫星电话卡）
    /// 1. 中国电信号段: 133, 1349(卫), 141(物), 149(物), 153, 162(虚), 1700(虚), 1701(虚), 1702(虚), 173, 1740(0-5)(卫), 177, 180, 181, 189, 190(5G), 191, 193, 199
    /// 2. 中国联通号段: 130, 131, 132, 140(物), 145, 146(物), 155, 156, 166, 167(虚), 1704(虚), 1707(虚), 1708(虚), 1709(虚)，171(虚), 175, 176, 185, 186, 196(5G)
    /// 3. 中国移动号段: 134(0-8), 135, 136, 137, 138, 139, 144(物), 147, 148(物), 150, 151, 152, 157, 158, 159, 165(虚), 1703(虚), 1705(虚), 1706(虚), 172(物), 178, 182, 183, 184, 187, 188, 195, 197(5G), 198
    /// 4. 中国广电号段: 192(5G)
    /// 5. 中国交通通信信息中心: 1749(卫)
    /// 6. 工业和信息化部应急通信保障中心: 1740(6-9)(卫), 1741(0-2)(卫)
    /// </summary>
    /// <param name="value">源字符串</param>
    /// <returns>是否匹配成功</returns>
    public static bool IsPhoneNumber(this string value)
    {
        if (string.IsNullOrWhiteSpace(value) || value.Length != 11)
        {
            return false;
        }

        //验证3位号段
        var segment = value[..AppSettings.UNIT_THREE];
        if (PhoneNumberSegment.Contains(segment))
        {
            return true;
        }

        //验证4位号段
        segment = value[..AppSettings.UNIT_FOUR];
        if (PhoneNumberSegment.Contains(segment))
        {
            return true;
        }

        return false;
    }
    #endregion

    #region 校验身份证号码
    /// <summary>
    /// 从身份证号获取性别
    /// </summary>
    /// <param name="idCode">身份证号</param>
    /// <returns></returns>
    public static Gender GetGenderFromIdCode(this string idCode)
    {
        try
        {
            var gender = Convert.ToUInt32(idCode[^2]);
            return (gender % AppSettings.UNIT_TWO == AppSettings.UNIT_ZERO) ? Gender.Female : Gender.Male;
        }
        catch (Exception)
        {
            return Gender.Unknown;
        }
    }
    /// <summary>
    /// 从身份证号获取出生日期
    /// </summary>
    /// <param name="idCode">身份证号</param>
    /// <returns></returns>
    public static DateTime? GetBirthdayFromIdCode(this string idCode)
    {
        try
        {
            return idCode.Length switch
            {
                18 => DateTime.ParseExact(idCode[6..14], AppSettings.DEFAULT_FILE_DATE_FORMAT, CultureInfo.InvariantCulture),
                16 => DateTime.ParseExact(idCode[6..12], "yyMMdd", CultureInfo.InvariantCulture),
                _ => null
            };
        }
        catch (Exception)
        {
            return null;
        }
    }
    /// <summary>
    /// 根据GB11643-1999标准权威校验中国身份证号码的合法性
    /// </summary>
    /// <param name="idCode">身份证号码</param>
    /// <returns>是否匹配成功</returns>
    public static bool IsIdCode(this string idCode) => new IdentityCard(idCode).Validate();
    #endregion

    #region 校验统一社会信用代码
    public static bool IsUnifiedSocialCreditIdentifier(this string value) => new UnifiedSocialCreditIdentifier(value).Validate();
    #endregion

    /// <summary>
    /// 字符串掩码
    /// </summary>
    /// <param name="value">字符串</param>
    /// <param name="mask">掩码符</param>
    /// <returns></returns>
    public static string? Mask([DisallowNull] this string value, char mask = '*')
    {
        value = value.Trim();
        if (string.IsNullOrWhiteSpace(value))
        {
            return value;
        }

        string masks = mask.ToString().PadLeft(AppSettings.UNIT_FOUR, mask);
        return value.Length switch
        {
            _ when value.Length >= 11 => Regex.Replace(value, @"(\w{3})\w*(\w{4})", $"$1{masks}$2"),
            _ when value.Length == 10 => Regex.Replace(value, @"(\w{3})\w*(\w{3})", $"$1{masks}$2"),
            _ when value.Length == 9 => Regex.Replace(value, @"(\w{2})\w*(\w{3})", $"$1{masks}$2"),
            _ when value.Length == 8 => Regex.Replace(value, @"(\w{2})\w*(\w{2})", $"$1{masks}$2"),
            _ when value.Length == 7 => Regex.Replace(value, @"(\w{1})\w*(\w{2})", $"$1{masks}$2"),
            _ when value.Length >= 2 && value.Length < 7 => Regex.Replace(value, @"(\w{1})\w*(\w{1})", $"$1{masks}$2"),
            _ => value + masks
        };
    }
    /// <summary>
    /// 邮箱掩码
    /// </summary>
    /// <param name="value">邮箱</param>
    /// <param name="mask">掩码</param>
    /// <returns></returns>
    public static string MaskEmail([DisallowNull] this string value, char mask = '*')
    {
        var index = value.LastIndexOf("@", StringComparison.OrdinalIgnoreCase);
        var subString = value[AppSettings.UNIT_ZERO..index];
        return !IsEmail(value) ? value : value.Replace(subString, Mask(subString, mask), StringComparison.CurrentCulture);
    }
}
